﻿using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace UnitTesting.Fixturing
{
	class VerboseConstructorSelector<T> : ConstructorSelectorBase<T>
	{
		readonly bool _isStrict;
		IOrderedEnumerable<ConstructorInfo> _orderdConstructors;

		internal bool IsStrict
		{
			get { return _isStrict; }
		}

		internal VerboseConstructorSelector(bool isStrict)
		{
			_isStrict = isStrict;
		}

		protected internal override ConstructorInfo Select(ConstructorInfo[] constructors)
		{
			SetOrderdConstructors(constructors);
			EnsureIsStrictVerboseConstructors();
			return GetFirstVerboseConstructor();
		}

		void SetOrderdConstructors(IEnumerable<ConstructorInfo> constructors)
		{
			_orderdConstructors = constructors.OrderByDescending(x => x.GetParameters().Length);
		}

		void EnsureIsStrictVerboseConstructors()
		{
			if (_isStrict
			    && IsMoreThanOneConstructors()
			    && IsSecondVerboseParamCountSameWith(GetVerboseParamCount()))
			{
				throw new TooManyMatchedConstructorException(typeof (T), GetVerboseParamCount());
			}
		}

		bool IsMoreThanOneConstructors()
		{
			return _orderdConstructors.Count() > 1;
		}

		int GetVerboseParamCount()
		{
			return _orderdConstructors.First().GetParameters().Length;
		}

		bool IsSecondVerboseParamCountSameWith(int verboseParamCount)
		{
			return verboseParamCount == _orderdConstructors.ElementAt(1).GetParameters().Length;
		}

		ConstructorInfo GetFirstVerboseConstructor()
		{
			return _orderdConstructors.First();
		}
	}
}